package vip.liux.backend.infrastructure.jpa.pay;

import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
import vip.liux.contracts.models.pay.PayTransfer;
import vip.liux.contracts.repositories.pay.PayTransferRepository;
import vip.liux.contracts.shared.enums.pay.PayTransferStatus;

import java.time.LocalDateTime;
import java.util.List;

public interface PayTransferJpaRepository extends JpaRepository<PayTransfer, Long>, PayTransferRepository, JpaSpecificationExecutor<PayTransfer> {


    /**
     * 根据 ID 和 whereStatus 条件，更新 status 字段
     *
     * @param id          主键ID
     * @param whereStatus 条件当前状态
     * @param status      需要更新的新状态
     * @return 影响行数
     */
    @Override
    @Transactional
    @Modifying
    @Query("UPDATE PayTransfer t SET t.status = :status WHERE t.id = :id AND t.status = :whereStatus")
    int updateByIdAndStatus(@Param("id") Long id,
                            @Param("whereStatus") PayTransferStatus whereStatus,
                            @Param("status") PayTransferStatus status);

    /**
     * 根据 ID 和 whereStatus 条件，更新 status、channelPackageInfo 字段
     *
     * @param id                 主键ID
     * @param whereStatus        条件当前状态
     * @param status             需要更新的新状态
     * @param channelPackageInfo 需要更新的渠道包信息
     * @return 影响行数
     */
    @Override
    @Transactional
    @Modifying
    @Query("UPDATE PayTransfer t SET t.status = :status, t.channelPackageInfo = :channelPackageInfo " +
            "WHERE t.id = :id AND t.status = :whereStatus")
    int updateByIdAndStatus(@Param("id") Long id,
                            @Param("whereStatus") PayTransferStatus whereStatus,
                            @Param("status") PayTransferStatus status,
                            @Param("channelPackageInfo") String channelPackageInfo);

    /**
     * 根据 ID 和 whereStatus列表条件，更新 status、successTime、channelTransferNo、channelNotifyData 字段
     *
     * @param id                主键ID
     * @param whereStatus       条件当前状态（可为多个）
     * @param status            需要更新的新状态
     * @param successTime       成功时间
     * @param channelTransferNo 渠道转账流水号
     * @param channelNotifyData 渠道通知数据
     * @return 影响行数
     */
    @Override
    @Transactional
    @Modifying
    @Query("UPDATE PayTransfer t SET t.status = :status, t.successTime = :successTime, " +
            "t.channelTransferNo = :channelTransferNo, t.channelNotifyData = :channelNotifyData " +
            "WHERE t.id = :id AND t.status IN :whereStatus")
    int updateByIdAndStatus(@Param("id") Long id,
                            @Param("whereStatus") List<PayTransferStatus> whereStatus,
                            @Param("status") PayTransferStatus status,
                            @Param("successTime") LocalDateTime successTime,
                            @Param("channelTransferNo") String channelTransferNo,
                            @Param("channelNotifyData") String channelNotifyData);

    /**
     * 根据 ID 和 whereStatus列表条件，更新 status、channelTransferNo、json、channelErrorCode、channelErrorMsg 字段
     *
     * @param id                主键ID
     * @param whereStatus       条件当前状态（可为多个）
     * @param status            需要更新的新状态
     * @param channelTransferNo 渠道转账流水号
     * @param channelNotifyData JSON内容
     * @param channelErrorCode  渠道错误码
     * @param channelErrorMsg   渠道错误信息
     * @return 影响行数
     */
    @Override
    @Transactional
    @Modifying
    @Query("UPDATE PayTransfer t SET t.status = :status, t.channelTransferNo = :channelTransferNo, t.channelNotifyData = :channelNotifyData, " +
            "t.channelErrorCode = :channelErrorCode, t.channelErrorMsg = :channelErrorMsg " +
            "WHERE t.id = :id AND t.status IN :whereStatus")
    int updateByIdAndStatus(@Param("id") Long id,
                            @Param("whereStatus") List<PayTransferStatus> whereStatus,
                            @Param("status") PayTransferStatus status,
                            @Param("channelTransferNo") String channelTransferNo,
                            @Param("channelNotifyData") String channelNotifyData,
                            @Param("channelErrorCode") String channelErrorCode,
                            @Param("channelErrorMsg") String channelErrorMsg);

    @Override
    default Page<PayTransfer> findPage(String transferNo, Long appId, String channelCode, String merchantTransferId, PayTransferStatus status, String userName, String userAccount, String channelTransferNo, LocalDateTime start, LocalDateTime end, Pageable page) {
        return findAll((root, query, criteriaBuilder) -> {
            var predicates = criteriaBuilder.conjunction();

            if (StringUtils.isNotBlank(transferNo)) {
                predicates = criteriaBuilder.and(predicates, criteriaBuilder.equal(root.get("transferNo"), transferNo));
            }

            if (appId != null) {
                predicates = criteriaBuilder.and(predicates, criteriaBuilder.equal(root.get("appId"), appId));
            }

            if (StringUtils.isNotBlank(channelCode)) {
                predicates = criteriaBuilder.and(predicates, criteriaBuilder.equal(root.get("channelCode"), channelCode));
            }

            if (StringUtils.isNotBlank(merchantTransferId)) {
                predicates = criteriaBuilder.and(predicates, criteriaBuilder.equal(root.get("merchantTransferId"), merchantTransferId));
            }

            if (status != null) {
                predicates = criteriaBuilder.and(predicates, criteriaBuilder.equal(root.get("status"), status));
            }

            if (StringUtils.isNotBlank(userName)) {
                predicates = criteriaBuilder.and(predicates, criteriaBuilder.equal(root.get("userName"), userName));
            }

            if (StringUtils.isNotBlank(userAccount)) {
                predicates = criteriaBuilder.and(predicates, criteriaBuilder.equal(root.get("userAccount"), userAccount));
            }

            if (StringUtils.isNotBlank(channelTransferNo)) {
                predicates = criteriaBuilder.and(predicates, criteriaBuilder.equal(root.get("channelTransferNo"), channelTransferNo));
            }

            if (start != null) {
                predicates = criteriaBuilder.and(predicates, criteriaBuilder.greaterThanOrEqualTo(root.get("creationTime"), start));
            }

            if (end != null) {
                predicates = criteriaBuilder.and(predicates, criteriaBuilder.lessThanOrEqualTo(root.get("creationTime"), end));
            }
            return predicates;
        }, PageRequest.of(page.getPageNumber(), page.getPageSize()));
    }
}